package com.klondike.common;

import static com.klondike.common.Utils.hasValue;
import static com.klondike.common.Utils.isEmpty;
import static com.klondike.common.Utils.isNullOrBlank;
import static com.klondike.common.Utils.notNull;
import static com.klondike.common.Utils.trimToNull;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;

import java.io.BufferedReader;
import java.io.Reader;
import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

import org.junit.Test;

import com.klondike.common.TestProperties.PropertyFileBuilder;
import com.klondike.common.concurrent.MutuxPool;
import com.klondike.common.io.IOUtils;

public class TheUtils {

    @Test
    public void verifyIsBlankOrNull() throws Exception {
        assertTrue(isNullOrBlank(""));
        assertTrue(isNullOrBlank(null));

        assertFalse(isNullOrBlank("  "));
    }

    @Test
    public void verifyHasValue() throws Exception {
        assertTrue(hasValue("  "));
        assertTrue(notNull("S"));
        assertFalse(hasValue(null));
        assertFalse(notNull("  "));
        assertFalse(notNull(null));

    }

    @Test
    public void trimEmptyStingToNull() throws Exception {
        assertNull(trimToNull("  "));
        assertNull(trimToNull(null));
        assertNull(trimToNull(""));
    }

    @Test
    public void searchValueInIntArray() throws Exception {
        int[] values = new int[] { 1, 34, 46, 4, Integer.MAX_VALUE };

        assertTrue(Utils.in(34, values));
        assertTrue(Utils.in(Integer.MAX_VALUE, values));

        assertFalse(Utils.in(5, values));
        assertFalse(Utils.in(343, null));
        assertFalse(Utils.in(343, new int[0]));
    }

    @Test
    public void searchValueInObjectArray() throws Exception {
        Object[] values = new Object[] { "A", "C", "F", "D", "E" };
        assertTrue(Utils.in("A", values));
        assertTrue(Utils.in("E", values));

        assertFalse(Utils.in("B", values));
        assertFalse(Utils.in("B", null));
        assertFalse(Utils.in("B", new Object[0]));
    }

    @Test
    public void areEquals() throws Exception {
        assertTrue(Utils.equals("O1", "O1"));
        assertTrue(Utils.equals(null, null));

        assertFalse(Utils.equals(null, "O2"));
        assertFalse(Utils.equals("O1", null));
        assertFalse(Utils.equals("O1", "O2"));
        assertFalse(Utils.equals(new Object(), new Object()));

    }

    @Test
    public void nullHasHashCode() throws Exception {
        assertEquals(0, Utils.hashCode(null));
        assertFalse(Utils.equals(0, Utils.hashCode(new Object())));
    }

    @Test
    public void nullSafeCompare() throws Exception {
        assertEquals(-1, Utils.compare("A", "B"));
        assertEquals(-1, Utils.compare(null, "B"));
        assertEquals(0, Utils.compare("A", "A"));
        assertEquals(1, Utils.compare("A", null));
        assertEquals(1, Utils.compare("B", "A"));

    }

    @Test
    public void areArrayEmpty() throws Exception {
        assertTrue(isEmpty(new Object[0]));

        assertTrue(isEmpty((Object[]) null));
        assertFalse(isEmpty(new Object[] { "_someObject" }));
    }

    @Test
    public void areCollectionEmpty() throws Exception {
        assertTrue(isEmpty(Collections.emptySet()));

        assertTrue(isEmpty((Collection<?>) null));
        assertFalse(isEmpty(Collections.singleton("_someObject")));

        assertFalse(isEmpty(Collections.<String> singleton(null)));
    }

    @Test
    public void areObjectsEmpty() throws Exception {
        Object o = null;
        assertTrue(isEmpty(o));
        o = new Object();
        assertFalse(isEmpty(o));

        Set<Object> set = new HashSet<Object>();
        assertTrue(isEmpty((Object) set));
        set.add("SomeObject");
        assertFalse(isEmpty((Object) set));

        o = new Object[] {};
        assertTrue(isEmpty(o));
        o = new Object[] { "SomeObject" };
        assertFalse(isEmpty(o));

        HashMap<Object, Object> map = new HashMap<Object, Object>();
        assertTrue(isEmpty(map));
        map.put("A", "B");
        assertFalse(isEmpty(map));

        o = "";
        assertTrue(isEmpty(o));
        o = "A";
        assertFalse(isEmpty(o));
    }

    @Test
    public void compareCollections() throws Exception {
        Set<String> collection1 = Utils.newTreeSet("A", "B", "C");
        Set<String> collection2 = Utils.newTreeSet("A", "B", "C");

        // same object
        assertEquals(0, Utils.compare(collection1, collection1));
        // same content
        assertEquals(0, Utils.compare(collection1, collection2));

        // second has +1 element
        collection2.add("D");
        assertEquals(-1, Utils.compare(collection1, collection2));

        // first has +1 element
        collection1.add("D");
        collection1.add("E");
        assertEquals(1, Utils.compare(collection1, collection2));

        collection2.add("F");
        assertEquals(-1, Utils.compare(collection1, collection2));

    }

    @Test
    public void canReturnSubsetOfProperties() throws Exception {
        PropertyFileBuilder builder = PropertyFileBuilder.newBuilder();

        builder.add("jndi.contextFactoryClass", "SomeFactory");
        builder.add("jndi.factoryLookupMethod", "Discovery");
        builder.add("jdbc.user", "JDBCUser");
        builder.add("jdbc.password", "JDBCPassword");
        builder.add("jndi.url", "SomeUrl");
        builder.add("jndi.username", "UserName");
        builder.add("jndi.password", "password");

        Properties config = new Properties();
        config.load(builder.toReader());

        Properties jdbcConfig = Utils.subsetOf(config, "jdbc");

        assertEquals(7, config.size());
        assertEquals(2, jdbcConfig.size());

        Properties jndiConfig = Utils.subsetOf(config, "jndi");
        assertEquals(5, jndiConfig.size());
        assertEquals(7, config.size());

        Properties allConfig = Utils.subsetOf(config, "");
        assertEquals(7, allConfig.size());

        Properties jndiConfigWithPrefix = Utils.subsetOf(config, "jndi", false);
        assertEquals(5, jndiConfig.size());
        assertEquals("SomeUrl", jndiConfigWithPrefix.getProperty("jndi.url"));
    }

    @Test
    public void givePairOfAnything() throws Exception {
        String e1 = "SomePairElement1";
        Integer e2 = Integer.valueOf(100);
        SimpleEntry<String, Integer> pair = Utils.newPair(e1, e2);

        assertEquals(e1, pair.getFirst());
        assertEquals(e2, pair.getSecond());
    }

    @Test
    public void ifNullTest() throws Exception {
        Object o1 = new Object();
        Object alternate = new Object();

        assertSame(o1, Utils.ifNull(o1, alternate));

        o1 = null;

        assertSame(alternate, Utils.ifNull(o1, alternate));
    }

    @Test
    public void verifyTruePrecondition() throws Exception {
        Utils.checkCondition(true, "msg");
    }

    @Test
    public void verifyNotNull() throws Exception {
        Utils.checkNull(new Object(), "msg");
        assertTrue(true);
    }

    @Test(expected = IllegalArgumentException.class)
    public void verifyNull() throws Exception {
        Utils.checkNull(null, "msg");
    }

    @Test(expected = UnsupportedOperationException.class)
    public void cannotChangePairValue() throws Exception {
        String e1 = "SomePairElement1";
        Integer e2 = Integer.valueOf(100);
        SimpleEntry<String, Integer> pair = Utils.newPair(e1, e2);
        pair.setValue(Integer.valueOf(101));
    }

    @Test(expected = IllegalArgumentException.class)
    public void verifyFalsePrecondition() throws Exception {
        Utils.checkCondition(false, "msg");
    }

    @Test(expected = IllegalArgumentException.class)
    public void verifyIllegalUnclosedQuotedCsvLine() throws Exception {
        Utils.parseCsvLine("\"SOMETHIN,");
    }

    @Test(expected = IllegalArgumentException.class)
    public void verifyIllegalSeparatedQuotedCsvLine() throws Exception {
        Utils.parseCsvLine("\"SOMETHIN\" NetToken ");
    }

    @Test
    public void normaliseText() throws Exception {
        verifyNormalizedText("", "");
        verifyNormalizedText(null, "");

        String normalized = "A string of words satisfying the 'grammatical  rules' of a language make a sentence.";
        String sentence = "A string    of    words satisfying the 'grammatical  rules' of a   \n\t"
                + " language make a sentence.\n\n";

        verifyNormalizedText(sentence, normalized);

    }

    private void verifyNormalizedText(String paragraph, String expected) {
        String normailzed = Utils.normalizeText(paragraph);

        assertEquals(expected, normailzed);
    }

    @Test
    public void loadTestCsvDataVerify() throws Exception {
        Reader reader = IOUtils.getResourceAsReader(getClass(), "TheUtilsCsv.txt");
        BufferedReader bufferReader = IOUtils.newBufferedReader(reader);
        String rawLine = null;
        try {
            while ((rawLine = bufferReader.readLine()) != null) {
                if (rawLine.length() > 0 && rawLine.charAt(0) != '#') {
                    String[] lines = rawLine.split("=>");
                    if (lines.length == 2) {
                        String csvLine = lines[0];
                        String[] tokens = lines[1].split("::");
                        for (int i = 0; i < tokens.length; i++) {
                            // remove ( and )
                            tokens[i] = tokens[i].substring(1, tokens[i].length() - 1);
                        }
                        String[] actuals = Utils.parseCsvLine(csvLine);

                        assertArrayEquals("Failed :" + rawLine, tokens, actuals);
                    }
                }
            }
        } finally {
            IOUtils.closeQuietly(reader);
        }
    }

    @Test
    public void canParseNullCsvLine() throws Exception {
        assertArrayEquals(new String[0], Utils.parseCsvLine(null));
    }

    @Test
    public void canParseACsvLine() throws Exception {
        String ln = "The,\"CSV\"  ,format,is,very,common,data,format.";
        String[] tokens = Utils.parseCsvLine(ln);

        assertEquals(8, tokens.length);
        assertEquals("CSV", tokens[1]);
        assertEquals("format.", tokens[7]);
    }

    @Test
    public void getTheClassFileSourceForGivenClassName() throws Exception {
        URL junitJarUrl = Utils.getSourceUrlFor(Test.class);
        assertNotNull(junitJarUrl);
        assertTrue(junitJarUrl.getFile().contains("junit"));
    }

    @Test
    public void mutuxPool() throws Exception {
        MutuxPool<String> keyBasedMutuxPool = Utils.newMutuxPool();
        keyBasedMutuxPool.lockFor("Aa");
        
        assertTrue(keyBasedMutuxPool.isLockedFor("Aa"));
        assertFalse(keyBasedMutuxPool.isLockedFor("BB"));
        
        keyBasedMutuxPool.unlockFor("Aa");
        keyBasedMutuxPool.lockFor("BB");
        
        assertFalse(keyBasedMutuxPool.isLockedFor("Aa"));
        assertTrue(keyBasedMutuxPool.isLockedFor("BB"));
        
        
        
    }
}
